package org.example.myprojectbackend.config;

import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.myprojectbackend.entity.RestBean;
import org.example.myprojectbackend.entity.dto.Account;
import org.example.myprojectbackend.entity.vo.response.AuthorizeVO;
import org.example.myprojectbackend.filter.JwtAuthorityFilter;
import org.example.myprojectbackend.service.AccountService;
import org.example.myprojectbackend.utils.JwtUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.filter.RequestContextFilter;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Repeatable;
import java.nio.charset.Charset;

/**
 * @ClassName SecurityConfiguration
 * @Author liupanpan
 * @Date 2025/1/3
 * @Description SpringSecurity相关配置
 */
@Configuration
public class SecurityConfiguration {

    @Resource
    JwtUtils jwtUtils;
    @Resource
    JwtAuthorityFilter jwtFilter;

    @Resource
    AccountService accountService;
    /**
     * 针对springsecurity6的新版配置方法
     * @param http 配置器
     * @return 自动构建的内置过滤器链
     * @throws Exception 可能的异常
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                //配置访问权限
                .authorizeHttpRequests(conf -> conf
                        .requestMatchers("/api/auth/**").permitAll()
                        .anyRequest().authenticated())  //除了上面的，任何请求都需要验证
                //配置登录(登录url，登录成功/失败操作)
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/auth/login")
                        .failureHandler(this::onAuthenticationFailure)
                        .successHandler(this::onAuthenticationSuccess))
                //退出登录配置（退出跳转的URL）
                .logout(conf -> conf
                        .logoutUrl("/api/auth/logout")
                        .logoutSuccessHandler(this::onLogoutSuccess))
                //未登录时访问页面 处理
                .exceptionHandling(conf -> conf
                        //用户的角色 无权限
                        .accessDeniedHandler(this::AccessProcess)
                        .authenticationEntryPoint(this::HandlerProcess))
                //配置csrf(使无效)
                .csrf(AbstractHttpConfigurer::disable)
                //配置session为无状态,让Security不去处理session
                .sessionManagement(conf -> conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    /**
     * 将多种类型的Handler整合到同一个方法中，包括：
     * -登录成功
     * -登录失败
     * -未登录/无权限 拦截
     * @param request 请求
     * @param response 响应
     * @param e 异常或是验证实体
     * @throws IOException 可能的异常
     * @throws ServletException
     */
    public void HandlerProcess(HttpServletRequest request,
                               HttpServletResponse response,
                               AuthenticationException e) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(RestBean.unauthorized(e.getMessage()).asJsonString());
    }
    //角色无权限访问页面
    public void AccessProcess(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException exception) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(RestBean.forbidden(exception.getMessage()).asJsonString());
    }

    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        //获取User信息,保存到jwt
        User user = (User) authentication.getPrincipal();
        Account account = accountService.findAccountByNameOrEmail(user.getUsername());
        String token = jwtUtils.createJwt(user, account.getId(), account.getUsername());
        AuthorizeVO vo = new AuthorizeVO();
        vo.setRole(account.getRole());
        vo.setUsername(account.getUsername());
        vo.setToken(token);
        vo.setExpireTime(jwtUtils.getExpireDate());
        response.getWriter().write(RestBean.success(vo).asJsonString());
    }
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(RestBean.fail(401,exception.getMessage()).asJsonString());
    }

    /**
     * 退出登录，使令牌失效
     * 退出登录处理，将对应的Jwt令牌列入黑名单（借助Redis实现）不再使用
     * @param request
     * @param response
     * @param authentication 验证实体
     * @throws IOException
     */
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        String headerToken = request.getHeader("Authorization");

        if (jwtUtils.invalidateJwt(headerToken)) {
            //令牌已失效
            writer.write(RestBean.success("退出登录成功！").asJsonString());
            return;
        }
        writer.write(RestBean.fail(400,"退出登录失败！").asJsonString());
    }
}
